Skip to content

fix(esm): bundle ESM/CJS builds to avoid Node ESM resolution issues in SSR#403

Merged
cchanxzy merged 1 commit intocchanxzy:mainfrom
jontas:pr/fix-ssr-esm-module-resolution
Sep 29, 2025
Merged

fix(esm): bundle ESM/CJS builds to avoid Node ESM resolution issues in SSR#403
cchanxzy merged 1 commit intocchanxzy:mainfrom
jontas:pr/fix-ssr-esm-module-resolution

Conversation

@jontas
Copy link
Copy Markdown
Contributor

@jontas jontas commented Sep 24, 2025

Summary

This PR updates the build to emit single-file bundles for both ESM and CJS. It removes extensionless internal imports from the ESM output that break SSR under Node’s ESM resolver.

The published ESM entry dist/esm/index.js previously referenced internal files using extensionless relative imports (e.g., ./components/CurrencyInput). When a dev SSR tool (e.g., Vite with React Router) externalizes the package, Node tries to resolve those imports directly and fails because Node ESM requires explicit file extensions.

This surfaced as: “Cannot find module .../dist/esm/components/CurrencyInput imported from .../dist/esm/index.js”.

Solution

  • Switch to single-file bundles for both ESM and CJS:
  • ESM: entryPoints: ['src/index.ts'], outfile: 'dist/esm/index.js', bundle: true, format: 'esm', platform: 'neutral', external: ['react'], minify: true.
  • CJS: outfile: 'dist/cjs/index.js', bundle: true, format: 'cjs', platform: 'neutral', external: ['react'], minify: true.
  • This removes all internal relative imports from the ESM entry, so Node never needs to resolve extensionless paths.
  • Bundle size impact is negligible.

Minimal Reproduction

  • Create a new React Router SSR app: npx create react-router@latest
  • Install the current package: pnpm add react-currency-input-field@4.0.1
  • Add to the welcome/home route: import CurrencyInput from 'react-currency-input-field'
  • Add a <CurrencyImport> component somewhere on the page
  • Run dev SSR (React Router + Vite): pnpm dev
  • Load page and observe error:
  • Build this branch of the library, install the built package (via file/tarball or pnpm link) into the same minimal app, and run dev again. The page renders without SSR errors.

@jontas jontas force-pushed the pr/fix-ssr-esm-module-resolution branch from 4a57d06 to b934988 Compare September 24, 2025 11:24
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Sep 24, 2025

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (396d57b) to head (fda771d).
⚠️ Report is 28 commits behind head on main.
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #403   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           15        15           
  Lines          414       391   -23     
  Branches       156       174   +18     
=========================================
- Hits           414       391   -23     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cchanxzy
Copy link
Copy Markdown
Owner

Hi @jontas Thanks for this PR, I did not notice this when I change the build config oops.

Changing platform: 'browser' to platform: 'neutral' seems to work for me when I try reproduce.

Does this work for you?

If so, I'd prefer keeping the build config the same and only changing the one line.

@jontas jontas force-pushed the pr/fix-ssr-esm-module-resolution branch from b934988 to fda771d Compare September 25, 2025 00:23
@jontas
Copy link
Copy Markdown
Contributor Author

jontas commented Sep 25, 2025

@cchanxzy Thanks for taking a look! I removed a few changes but the current commit has the minimal changeset that worked for me. At first it seemed like changing platform to neutral was enough but eventually I realized my local setup (using "react-currency-input-field": "file:../react-currency-input-field", in the react-router package.json) was not using the files in dist and the issue does not reproduce with the unbuilt files.

The files property in your package.json provides options for both dist and src and I think vite in react-router was choosing src. Once I updated files to just point to dist I was able to repro the error more consistently, and the key changes are external: ['react'] and bundle:true. Those settings require using outfile and not outdir.

If you have other suggestions I'm happy to give them a try 🙏

@cchanxzy cchanxzy merged commit 81d6b94 into cchanxzy:main Sep 29, 2025
2 checks passed
@github-actions
Copy link
Copy Markdown

🎉 This PR is included in version 4.0.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

@cchanxzy
Copy link
Copy Markdown
Owner

Thanks @jontas for this!

I was experimenting and seeing if it could work without bundling, but seems like bundling was the only way.

Merged now, thanks for your contribution! And spotting the issue and fixing too :)

@jontas
Copy link
Copy Markdown
Contributor Author

jontas commented Sep 29, 2025

🥳 thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants